/******************************************************************************************************
Copyright (C) 2013 Mestrelab Research S.L. All rights reserved.

This file is part of the Mnova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*globals Application, MessageBox, CommonSlots, settings, Dir, qsTr, MassItem, ProgressDialog, DBPlugin, MessageBox, Str, serialization, File, TextStream, Dialog, GroupBox, ComboBox, MnUi, gc*/
/*jslint plusplus: true, indent: 4*/

function dbSearchMSInChrom() {
    "use strict";
    if (Application.version.revision < 8331) {
        MessageBox.critical("You are running " + Application.name + " " + Application.version.full + ".\nThis script function can be run with MNova version 7.0.0 and higher.");
        return;
    }

    var reportFolder, reportDir, msItem, dbName, scoring, tolerance, toleranceInDalton, maxHits, minHitQuality,
        selMode, plot, peaks_list, hits_search, peaks, progress, threshold, client, query, peakHits,
        hits, rt_ranges, i, j, k,
        HITS = 5,  //Default max hits per peak
        MINHITQUALITY = 800, //Default min hit quality
    //Registry values
        ScoringKey = "MSSearch/Scoring",
        ToleranceKey = "MSSearch/Tolerance",
        ToleranceUnitKey = "MSSearch/Tolerance Unit",
        SelModeKey = "MSSearch/Selection Mode",
        HitsPerPeakKey = "MSSearch/Hits Per Peak",
        MinHitQualityKey = "MSSearch/Min Hit Quality",
        ReportFolderKey = "MSSearch/Report Folder",
    //Load interface
        diag = Application.loadUiFile("ricares:dbSearchMSInChrom.ui"),
        showDialog = true;

    function report() {
        var header, body, log, units, h, hit, peakResult, currentDoc, dw, molecule,
            resFileXhtml, resXhtml, strResXhtml, resFileTxt, resTxt,
            startDate = Str.date2ISOString(new Date()),
            startDateDir = startDate.replace(/:/g, ".");

        reportDir.mkdir(startDateDir);
        reportDir.cd(startDateDir);
        reportDir.mkdir("images");

        //var dir = new Dir(resultsPath);
        header = '<?xml version="1.0" encoding="UTF-8"?>'
            + '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
            + '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">'
            + '<head>'
            + '  <title>MS Search</title>'
            + '  <style>'
            + '     img { width: 30%; height: auto; }'
            + '  </style>'
            + ' </head>';

        //body creation
        body = "<body>";
        body += "\n<h1>MS Search</h1>";
        body += "\n\t<h2>Parameters</h2>";
        body += '\n<table border="1" cellSpacing="0" cellPadding="4" width="20%">';
        body += '\n<tr style="background-color:silver">';
        body += '\n<td width="50%"><b>Parameter</b></td>';
        body += '\n<td width="50%"><b>Value</b></td></tr>';
        body += '\n<tr>';
        body += '\n<td>Scoring</td>';
        body += '\n<td>' +  scoring + '</td></tr>';
        body += '\n<tr>';
        body += '\n<td>Tolerance</td>';
        units = toleranceInDalton ? "Dalton" : "ppm";
        body += '\n<td>' + tolerance + " " + units + '</td></tr>';
        body += '\n<tr>';
        body += '\n<td>Selection Mode</td>';
        body += '\n<td>' + selMode + '</td></tr>';
        body += '\n<tr>';
        body += '\n<td>Minimal Hit Score</td>';
        body += '\n<td>' + minHitQuality + '</td></tr>';
        body += '\n<tr>';
        body += '\n<td>Max Hits Per Peak</td>';
        body += '\n<td>' + maxHits + '</td></tr>';
        body += '\n<tr>';
        body += '\n<table border="1" cellSpacing="0" cellPadding="4">';
        body += '\n</table>';
        body += '\n<h2>Results Table</h2>';
        body += '\n<table border="1" cellSpacing="0" cellPadding="4" width="80%">';
        body += '\n<tr style="background-color:silver">';
        body += '\n<th><b>Record</b></th>';
        body += '\n<th><b>Database Name</b></th>';
        body += '\n<th><b>Score</b></th>';
        body += '\n<th><b>Peak</b></th>';
        body += '\n<th><b>Structure</b></th>';
        body += '\n<th><b>Spectrum</b></th></tr>';

        //log
        log = "";
        log += "MS Search";
        log += "\n\nParameters";
        log += "\nScoring: \t" + scoring;
        log += "\nTolerance: \t" + tolerance + " " + units;
        log += "\nSelection Mode: \t" + selMode;
        log += "\nMinimal Hit Score: \t" + minHitQuality;
        log += "\nMax Hits Per Peak: \t" + maxHits;
        log += "\n\nResults";
        log += '\nRecord\tDatabase Name\tScore\tPeak\tStructure\n';

        for (h = 0; h < hits.length; h++) {
            hit = hits[h];
            peakResult = rt_ranges[h].start.x.toFixed(2) + " .. " + rt_ranges[h].end.x.toFixed(2);
            body += '<tr><td>' + hit.recordId()  + '</td><td>' + dbName + '</td><td>' + hit.score() + '</td><td>' + peakResult + '</td>';

            currentDoc = Application.mainWindow.activeDocument;
            dw = Application.mainWindow.newWindow();
            DBPlugin.getRecord(client, hit.recordId());
            if (dw.itemCount("Molecule") > 0) {
                molecule = dw.item(0, "Molecule");
                dw.newPage().cloneItems(molecule);
                dw.setSelectedPages([dw.curPage()]);
                serialization.save(reportDir.filePath("images/" + "structure" + h + ".png"), "png");
            }
            if (dw.itemCount("Mass Spectrum") > 0) {
                msItem = dw.item(0, "Mass Spectrum");
                dw.newPage().cloneItems(msItem);
                dw.setSelectedPages([dw.curPage()]);
                serialization.save(reportDir.filePath("images/" + "msitem" + h + ".png"), "png");
            }
            body += '<td align="center"><img src="file:///' + reportDir.filePath("images/" + 'structure' + h + '.png' + '">') + '</td>'
                +  '<td align="center"><img src="file:///' + reportDir.filePath("images/" + 'msitem' + h + '.png' + '">') + '</td>' + ' </tr>';
            log += '\t' + hit.recordId() + '\t' + dbName + '\t' + hit.score() + '\t' + peakResult + '\t' + reportDir.filePath("images/" + "structure" + h + ".png") + '\n';
            dw.close();
            Application.mainWindow.activeDocument = currentDoc;
            gc();
        }
        body += '</table></body></html>';
        resFileXhtml = new File(reportDir.filePath("result.html"));
        resFileXhtml.open(File.ReadWrite);
        resXhtml = new TextStream(resFileXhtml);
        strResXhtml = header + body;
        resXhtml.write(strResXhtml);
        resXhtml.flush();
        resFileXhtml.close();
        resFileTxt = new File(reportDir.filePath("log.txt"));
        resFileTxt.open(File.ReadWrite);
        resTxt = new TextStream(resFileTxt);
        resTxt.write(log);
        resTxt.flush();
        resFileTxt.close();
    }

    function chooseDB() {
        var dbNames, dbi, result,
            dbDialog = new Dialog(qsTr("Select Database")),
            gbDBChoose = new GroupBox(),
            cmbDB = new ComboBox(),
            done = false;

        Application.mainWindow.doAction("dbConnect");
        if (!DBPlugin.client.connected()) {
            return;
        }
        gbDBChoose.title = qsTr("Database");
        dbNames = DBPlugin.client.databases();
        for (dbi = 0; dbi < dbNames.length; dbi++) {
            cmbDB.addItem(dbNames[dbi]);
        }
        cmbDB.editable = false;
        gbDBChoose.add(cmbDB);
        dbDialog.add(gbDBChoose);
        while (!done) {
            if (!dbDialog.exec()) {
                return;
            }
            result = cmbDB.currentText;
            if (DBPlugin.client.openDatabase(result)) {
                done = true;
            } else {
                MessageBox.critical("Could not open database " + result, MessageBox.Ok);
            }
        }
        return result;
    }

    diag.widgets.btFolder.clicked.connect(diag.widgets.leFolder, CommonSlots.onOpenDir);
    //Load Setting Values
    diag.widgets.cbScoring.currentIndex = settings.value(ScoringKey, 0);
    diag.widgets.cbUnits.currentIndex = settings.value(ToleranceUnitKey, 0);
    diag.widgets.cbSelMode.currentIndex = settings.value(SelModeKey, 0);
    diag.widgets.sbTolerance.value = settings.value(ToleranceKey, 0.50);
    diag.widgets.sbHits.value = settings.value(HitsPerPeakKey, HITS);
    diag.widgets.sbMinHitQuality.value = settings.value(MinHitQualityKey, MINHITQUALITY);
    diag.widgets.leFolder.text = settings.value(ReportFolderKey, Dir.home());

    while (showDialog) {

        if (!diag.exec()) {
            return;
        }

        reportFolder = diag.widgets.leFolder.text;

        reportDir = new Dir(reportFolder);
        if (!reportDir || !reportDir.exists) {
            MessageBox.critical("Folder " + reportDir + " not exists");
            diag.widgets.leFolder.setFocus();
        } else {
            showDialog = false;
        }
    }

    msItem = Application.mass.activeItem();
    if (!msItem) {
        MessageBox.critical(qsTr("Mass item is needed"));
    } else {
        settings.setValue(ScoringKey, diag.widgets.cbScoring.currentIndex);
        settings.setValue(ToleranceUnitKey, diag.widgets.cbUnits.currentIndex);
        settings.setValue(ToleranceKey, diag.widgets.sbTolerance.value);
        settings.setValue(HitsPerPeakKey, diag.widgets.sbHits.value);
        settings.setValue(MinHitQualityKey, diag.widgets.sbMinHitQuality.value);
        settings.setValue(SelModeKey, diag.widgets.cbSelMode.currentIndex);
        settings.setValue(ReportFolderKey, diag.widgets.leFolder.text);

        dbName = chooseDB();
        if (dbName) {
            scoring = diag.widgets.cbScoring.currentText;
            tolerance = diag.widgets.sbTolerance.value;
            toleranceInDalton = diag.widgets.cbUnits.currentIndex === 0;
            maxHits = diag.widgets.sbHits.value;
            minHitQuality = diag.widgets.sbMinHitQuality.value;
            selMode = diag.widgets.cbSelMode.currentText;

            if (diag.widgets.cbSelMode.currentIndex === 0) {
                msItem.selectionMode = MassItem.SpectrumSelMode.Manual;
            } else if (diag.widgets.cbSelMode.currentIndex === 1) {
                msItem.selectionMode = MassItem.SpectrumSelMode.Peak;
            } else if (diag.widgets.cbSelMode.currentIndex === 2) {
                msItem.selectionMode = MassItem.SpectrumSelMode.PeakSub;
            }

            plot = msItem.plot(0);
            peaks_list = plot.peaks;
            hits_search = [];
            peaks = [];

            progress = new ProgressDialog();
            progress.minimumDuration = 0;
            progress.setRange(0, peaks_list.length);
            progress.labelText = qsTr("Searching...");
            progress.show();

            for (i = 0; i < peaks_list.length && !progress.wasCanceled; i++) {
                progress.value = i;
                if (msItem.selectionMode === MassItem.SpectrumSelMode.Manual) {
                    msItem.selectSpectra(plot, peaks_list[i].start.x, peaks_list[i].end.x);
                } else {
                    msItem.selectSpectra(plot, peaks_list[i].center.x);
                }
                msItem.update();

                threshold = (minHitQuality - 1) / 1000.0;
                query = msItem.dbQuery(scoring, threshold, tolerance, toleranceInDalton);
                client = DBPlugin.client;
                peakHits = DBPlugin.search(client, query, "MASSDATA", "", "", maxHits, 100, minHitQuality);
                                //search ( DBClient aDBClient, String aQuery, [String aSearchType = "TEXT"], [String aItemType = ""], [String aFieldName = ""],
                //          [Number aMaxHits = 1000], [Number aMaxRecords = 100], [Number aMinHitQuality = 1000] ): DBSearchHits

                if (peakHits.count > 0) {
                    hits_search.push(peakHits);
                    peaks.push(peaks_list[i]);
                }
                gc();
            }
            progress.close();
            if (hits_search.length === 0) {
                MessageBox.critical(qsTr("No matches were found"));
            } else {
                hits = [];
                rt_ranges = [];

                for (j = 0; j < hits_search.length; j++) {
                    for (k = 0; k < hits_search[j].count; k++) {
                        hits.push(hits_search[j].at(k));
                        rt_ranges.push(peaks[j]);
                    }
                }
                report();
		if (typeof Application.openUrl === 'function') {
	                Application.openUrl(reportDir.filePath("result.html"));
		}
                else {
			MessageBox.information("Results have been written to " + reportDir.filePath("result.html") + ".");
		}
			
            }
        }
    }
}

if (this.MnUi && MnUi.scripts_db) {
    MnUi.scripts_db.scripts_db_SearchMSinChromatogram = dbSearchMSInChrom;
}
